using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Reflection;
using System.Threading;
using System.Diagnostics;

namespace PotonCapture
{
	public abstract class HookBase : IDisposable
	{
		public enum HookTypes
		{
            //Keyboard = 2,
            //Mouse = 7,
            KeyboardLowLevel = 13,
            MouseLowLevel = 14,
        }

        public delegate int HookHandler(int nCode, IntPtr wParam, IntPtr lParam);

#region extern

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int SetWindowsHookEx(int idHook, HookHandler lpfn,
        IntPtr hInstance, int threadId);
        
        [DllImport("user32.dll", CharSet = CharSet.Auto,
         CallingConvention = CallingConvention.StdCall)]
        public static extern bool UnhookWindowsHookEx(int idHook);

        [DllImport("user32.dll", CharSet = CharSet.Auto,
         CallingConvention = CallingConvention.StdCall)]
        public static extern int CallNextHookEx(int idHook, int nCode,
        IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto,
         CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr GetModuleHandle(String lpModuleName);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto,
         CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr GetCurrentThreadId();
        
#endregion

        protected HookTypes _hookType;

        public HookTypes HookType
        {
            get { return _hookType; }
        }
	
		protected int _beforeHookHandle;
		protected GCHandle _hookHandle;
        protected HookHandler _hookDelegate;
        protected bool _global;

        public bool IsGlobal
        {
            get { return _global; }
            set { _global = value; }
        }
	

		protected HookBase(HookTypes hookType, bool globalHook)
		{
            _hookDelegate += OnHook;
            _hookType = hookType;
            _global = globalHook;
            Dispose();
        }

        protected void AllockHandle()
        {
            _hookHandle = GCHandle.Alloc(_hookDelegate);
        }

        public void Hook()
        {
            Unhook();
            AllockHandle();
            _beforeHookHandle = SetWindowsHookExCall(_hookType, _global, _hookDelegate);
        }

        public void Unhook()
        {
            if (_beforeHookHandle != 0)
            {
                UnhookWindowsHookEx(_beforeHookHandle);
                _beforeHookHandle = 0;
            }
        }

		protected static int SetWindowsHookExCall(HookTypes hookType, bool globalHook, HookHandler hookDelegate)
		{
            int hook = 0;
            if (globalHook)
            {
                Process process = Process.GetCurrentProcess();
                ProcessModule module = process.MainModule;
                IntPtr moduleHandle = GetModuleHandle(module.ModuleName);
                hook = SetWindowsHookEx((int)hookType, hookDelegate, moduleHandle, 0);
            }
            else
            {
                IntPtr threadId = GetCurrentThreadId();
                hook = SetWindowsHookEx((int)hookType, hookDelegate, IntPtr.Zero, (int)threadId);
            }
            if (hook == 0)
            {
                int errorCode = Marshal.GetLastWin32Error();
                throw new Win32Exception(errorCode);
            }
			return hook;
		}

        protected virtual int OnHook(int nCode, IntPtr wParam, IntPtr lParam)
        {
            return CallNextHook(nCode, wParam, lParam);
        }

        protected virtual int CallNextHook(int code, IntPtr wParam, IntPtr lParam)
        {
            return CallNextHookEx(_beforeHookHandle, code, wParam, lParam);
        }

        public virtual void Dispose()
		{
            Unhook();
            if (_hookHandle.IsAllocated)
			{
				_hookHandle.Free();
			}
        }

		~HookBase()
		{
			Dispose();
		}
	}
}
